﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Compilation;
using umbraco.BusinessLogic;
using umbraco.cms.businesslogic.web;
using umbraco.IO;
using System.Text.RegularExpressions;
using umbraco;

namespace TheOutfield.UmbExt.AutoExport2DotNet
{
    public class AppBase : ApplicationBase
    {
        public AppBase()
        {
            DocumentType.AfterSave += new DocumentType.SaveEventHandler(DocumentType_ExportCode);
            DocumentType.AfterDelete += new DocumentType.DeleteEventHandler(DocumentType_ExportCode);
        }

        protected void DocumentType_ExportCode(DocumentType sender, EventArgs e)
        {
            var myMachineName = System.Environment.MachineName.ToLower();

            //Log.Add(LogTypes.Custom, -1, "AutoExport2DotNet: Machine Name = " + myMachineName);
            //Log.Add(LogTypes.Custom, -1, "AutoExport2DotNet: Mode = " + Config.Instance.Mode);
            //Log.Add(LogTypes.Custom, -1, "AutoExport2DotNet: DataContextName = " + Config.Instance.DataContextName);
            //Log.Add(LogTypes.Custom, -1, "AutoExport2DotNet: Namespace = " + Config.Instance.Namespace);
            //Log.Add(LogTypes.Custom, -1, "AutoExport2DotNet: Ouputs = " + Config.Instance.Outputs.Count);

            if (Config.Instance.Outputs.Count > 0 && Config.Instance.Outputs.ContainsKey(myMachineName))
            {
                var versionNo = Convert.ToInt32(Regex.Replace(GlobalSettings.CurrentVersion, "[^0-9]", "").PadRight(5, '0'));
                var exportPath = (versionNo <= 45000) ?
                    SystemDirectories.Data :
                    SystemDirectories.Media + "/exported-doctypes/";

                var inputAbsPath = IOHelper.MapPath(Path.Combine(exportPath, "I" + Config.Instance.DataContextName + ".txt"));
                var inputPocoPath = IOHelper.MapPath(Path.Combine(exportPath, Config.Instance.DataContextName + ".txt"));

                if (File.Exists(inputAbsPath))
                    File.Delete(inputAbsPath);

                if (File.Exists(inputPocoPath))
                    File.Delete(inputPocoPath);

                // Generate files
                var pg = new ExportCodeProxy();
                pg.Generate(Config.Instance.Mode,
                    Config.Instance.DataContextName,
                    Config.Instance.Namespace);

                // Get output
                var output = Config.Instance.Outputs[myMachineName];

                if (output.OutputFormat == Config.OutputFormat.CS)
                {
                    // Interfaces
                    if (Config.Instance.Mode == "abs" && File.Exists(inputAbsPath))
                    {
                        var outputAbsPath =
                            IOHelper.MapPath(Path.Combine(output.OutputDirectory,
                                                          "I" + Config.Instance.DataContextName + ".cs"));

                        if (File.Exists(outputAbsPath))
                            File.Delete(outputAbsPath);

                        File.Move(inputAbsPath, outputAbsPath);
                    }

                    // Pocos
                    if (File.Exists(inputPocoPath))
                    {
                        var outputPocoPath =
                            IOHelper.MapPath(Path.Combine(output.OutputDirectory,
                                                          Config.Instance.DataContextName + ".cs"));

                        if (File.Exists(outputPocoPath))
                            File.Delete(outputPocoPath);

                        File.Move(inputPocoPath, outputPocoPath);
                    }
                }
                else if (output.OutputFormat == Config.OutputFormat.DLL)
                {
                    if (File.Exists(inputPocoPath))
                    {
                        if (Config.Instance.Mode == "abs" && File.Exists(inputAbsPath))
                        {
                            CombineFiles(inputAbsPath, inputPocoPath);

                            File.Delete(inputAbsPath);
                        }

                        var outputPocoVirtualPath = Path.Combine(exportPath, Config.Instance.DataContextName + ".cs");
                        var outputPocoPath = IOHelper.MapPath(outputPocoVirtualPath);
                        var outputPocoDllPath = Path.Combine(output.OutputDirectory, Config.Instance.Namespace + "." + Config.Instance.DataContextName + ".dll");

                        if (File.Exists(outputPocoPath))
                            File.Delete(outputPocoPath);

                        File.Move(inputPocoPath, outputPocoPath);

                        CreateAssembly(outputPocoVirtualPath, outputPocoDllPath);

                        File.Delete(outputPocoPath);
                    }
                }
            }
        }

        protected void CreateAssembly(string inputCsPath, string outputDllPath)
        {
            var compiled = BuildManager.GetCompiledAssembly(inputCsPath);
            var compiledFiles = compiled.GetFiles(true);
            if (compiledFiles.Count() == 1)
            {
                foreach (FileStream fs in compiledFiles)
                {
                    BinaryReader reader = new BinaryReader(fs);
                    byte[] bytes = new byte[fs.Length];
                    int read;
                    reader.Read(bytes, 0, bytes.Length);
                    reader.Close();
                    fs.Close();

                    FileStream fs1 = new FileStream(outputDllPath, FileMode.Create, FileAccess.Write);
                    BinaryWriter bw = new BinaryWriter(fs1);
                    bw.Write(bytes);
                    bw.Close();
                    fs1.Close();
                }
            }
        }

        protected void CombineFiles(string src, string dest)
        {
            TextReader srcReader = new StreamReader(src);
            var srcContent = srcReader.ReadToEnd();
            srcReader.Close();

            var usingStatements = "";
            var usingRegex = new Regex(@"using\s[a-zA-Z0-9\.]*;", RegexOptions.IgnoreCase);
            var matches = usingRegex.Matches(srcContent);
            foreach (Match match in matches)
            {
                usingStatements += match.Value + Environment.NewLine;
            }
            srcContent = usingRegex.Replace(srcContent, "");

            TextReader destReader = new StreamReader(dest);
            var destContent = destReader.ReadToEnd();
            destReader.Close();

            var sw = File.CreateText(dest);
            sw.Write(usingStatements);
            sw.WriteLine("");
            sw.Write(destContent);
            sw.WriteLine("");
            sw.Write(srcContent);
            sw.Close();
        }
    }
}